#include <iostream>
#include <sys/epoll.h>
#include <unistd.h>
#include <cstdlib>
#include <string>
#include "Sock.hpp"

#define SIZE 128
#define NUM 64

static void Usage(std::string proc)
{
    std::cout << "Usage: " << proc << " port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }
    // 1、建立TCP监听socket
    uint port = atoi(argv[1]);
    int listen_sock = Sock::Socket();
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);

    // 2、创建epoll模型，获得epfd(文件描述符)
    int epfd = epoll_create(SIZE);

    // 3、先添加listen_sock和他所关心的事件添加到内核
    struct epoll_event ev;
    ev.events = EPOLLIN; //只关心读事件
    ev.data.fd = listen_sock;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_sock, &ev);

    // 4、事件循环
    volatile bool quit = false;
    struct epoll_event revs[NUM];
    while (!quit)
    {
        int timeout = -1; //-1表示阻塞式等待
        int n = epoll_wait(epfd, revs, NUM, timeout);
        switch (n)
        {
        case 0:
            std::cout << "time out" << std::endl;
            break;
        case -1:
            std::cout << "epoll error" << std::endl;
            break;
        default:
            std::cout << "有事件就绪" << std::endl;
            // 5、处理就绪事件
            for (int i = 0; i < n; i++)
            {
                int sock = revs[i].data.fd;
                std::cout << "文件描述符:" << sock << "事件就绪" << std::endl;
                if (revs[i].events & EPOLLIN)
                {
                    std::cout << "文件描述符:" << sock << "读事件就绪" << std::endl;
                    if (sock == listen_sock)
                    {
                        std::cout << "文件描述符:" << sock << "链接事件就绪" << std::endl;
                        // 5.1处理链接事件
                        int fd = Sock::Accept(listen_sock);
                        if (fd >= 0)
                        {
                            std::cout << "获取新链接成功:" << fd << std::endl;
                            //有链接到来不代表一定有数据到来，不能立即读取
                            struct epoll_event _ev;
                            _ev.events = EPOLLIN;
                            _ev.data.fd = fd;
                            //将新的fd托管给epoll
                            epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &_ev);
                            std::cout << "已将" << fd << "托管给epoll" << std::endl;
                        }
                        else
                        {
                            //获取连接失败
                        }
                    }
                    else
                    {
                        // 5.2正常的读取处理
                        std::cout << "文件描述符:" << sock << "正常事件就绪" << std::endl;
                    }
                }
                else if (revs[i].events & EPOLLOUT)
                {
                    //处理写事件
                }
                else
                {
                }
            }
            break;
        }
    }

    close(epfd);
    close(listen_sock);

    return 0;
}